/*********************************
 * jsmud: JavaScript MUD Kernel  *
 *          Mojodo, Inc.         *
 *        Sean Middleditch       *
 * Copyright (C) 2005,2007,2008  *
 *      All rights reserved.     *
 *   See COPYING for licensing.  *
 *********************************/

/* ==== SYSTEM INCLUDES ==== */

#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <arpa/telnet.h>
#include <sys/stat.h>
#include <dirent.h>

/* ==== LIBRARY INCLUDES ==== */

#include <sqlite3.h>
#include <zlib.h>

#define XP_UNIX 1
#define MOZILLA_1_8_BRANCH 1
#include <jsapi.h>
#undef XP_UNIX
#undef MOZILLA_1_8_BRANCH

/* ==== JSMUD HEADER ==== */

#include "jsmud.h"

/* ==== DECLARATIONS ==== */

JSAPI(js_file_get);
JSAPI(js_file_put);
JSAPI(js_file_isfile);
JSAPI(js_file_isdir);
JSAPI(js_file_delete);
JSAPI(js_file_list);

/* ==== SCRIPT BINDINGS - File ==== */

static JSFunctionSpec js_file_functions[] = {
	{"get", js_file_get, 1, 0, 0},
	{"put", js_file_put, 2, 0, 0},
	{"isfile", js_file_isfile, 1, 0, 0},
	{"isdir", js_file_isdir, 1, 0, 0},
	{"delete", js_file_delete, 1, 0, 0},
	{"list", js_file_list, 1, 0, 0},
	{NULL, 0, 0, 0, 0}
};

JSAPI(js_file_get)
{
	const char* path;
	char buf[4096];
	size_t rs;
	FILE* file;

	/* check args -- get path */
	if (!JS_ConvertArguments(cx, argc, argv, "s", &path))
		return JS_FALSE;

	/* open the file */
	if ((file = fopen(makepath(path), "r")) == NULL) {
		js_raise(cx, "Failed to open file `%s': %s", path, strerror(errno));
		return JS_FALSE;
	}

	/* create result string; make this growable for performance */
	*rval = STRING_TO_JSVAL(JS_NewGrowableString(cx, 0, 0));

	/* read in the file.  it sucks there is no API append function; we have
	 * to create a string for each buffer read, then append it to our growable
	 * result string */
	while (!feof(file)) {
		rs = fread(buf, 1, sizeof(buf), file);
		if (rs > 0)
			*rval = STRING_TO_JSVAL(JS_ConcatStrings(cx, JSVAL_TO_STRING(*rval), JS_NewStringCopyN(cx, buf, rs)));
	}

	/* close the file and make our growable string a regular string */
	fclose(file);
	JS_MakeStringImmutable(cx, JSVAL_TO_STRING(*rval));

	return JS_TRUE;
}

JSAPI(js_file_put)
{
	const char* path;
	const char* contents;
	size_t rs;
	size_t len;
	FILE* file;

	/* check args -- get path */
	if (!JS_ConvertArguments(cx, argc, argv, "ss", &path, &contents))
		return JS_FALSE;

	/* open the file */
	if ((file = fopen(makepath(path), "w")) == NULL) {
		js_raise(cx, "Failed to open file `%s': %s", path, strerror(errno));
		return JS_FALSE;
	}

	/* write out the file */
	len = strlen(contents);
	while (!feof(file) && len > 0) {
		rs = fwrite(contents, 1, len, file);
		contents += rs;
		len -= rs;
	}

	/* close the file and make our growable string a regular string */
	fclose(file);

	return JS_TRUE;
}

JSAPI(js_file_isfile)
{
	const char* path;
	struct stat st;

	/* check args -- get path */
	if (!JS_ConvertArguments(cx, argc, argv, "s", &path))
		return JS_FALSE;

	/* stat the path */
	if (stat(makepath(path), &st) != 0) {
		js_raise(cx, "Path does not exist or cannot be accessed `%s'", path);
		return JS_FALSE;
	}

	/* check if the path is a file */
	if (S_ISREG(st.st_mode))
		*rval = BOOLEAN_TO_JSVAL(JS_TRUE);
	else
		*rval = BOOLEAN_TO_JSVAL(JS_FALSE);

	return JS_TRUE;
}

JSAPI(js_file_isdir)
{
	const char* path;
	struct stat st;

	/* check args -- get path */
	if (!JS_ConvertArguments(cx, argc, argv, "s", &path))
		return JS_FALSE;

	/* stat the path */
	if (stat(makepath(path), &st) != 0) {
		js_raise(cx, "Path does not exist or cannot be accessed `%s'", path);
		return JS_FALSE;
	}

	/* check if the path is a directory */
	if (S_ISDIR(st.st_mode))
		*rval = BOOLEAN_TO_JSVAL(JS_TRUE);
	else
		*rval = BOOLEAN_TO_JSVAL(JS_FALSE);

	return JS_TRUE;
}

JSAPI(js_file_delete)
{
	const char* path;
	const char* fullpath;
	struct stat st;

	/* check args -- get path */
	if (!JS_ConvertArguments(cx, argc, argv, "s", &path))
		return JS_FALSE;
	fullpath = makepath(path);

	/* stat the path */
	if (stat(makepath(fullpath), &st) != 0) {
		js_raise(cx, "Path does not exist or cannot be accessed `%s'", path);
		return JS_FALSE;
	}

	/* check if the path is a directory; use rmdir on directories */
	if (S_ISDIR(st.st_mode)) {
		if (rmdir(fullpath) != 0) {
			js_raise(cx, "Directory cannot be deleted `%s'", path);
			return JS_FALSE;
		}
	/* check if the path is a file; use unlink on files */
	} else if (S_ISREG(st.st_mode)) {
		if (unlink(fullpath) != 0) {
			js_raise(cx, "File cannot be deleted `%s'", path);
			return JS_FALSE;
		}
	/* not a file or directory... error */
	} else {
		js_raise(cx, "Path is not a file or directory `%s'", path);
		return JS_FALSE;
	}

	return JS_TRUE;
}

JSAPI(js_file_list)
{
	const char* path;
	DIR* dir;
	struct dirent* d;
	JSObject* array;
	jsval name;
	jsint index;

	/* check args -- get path */
	if (!JS_ConvertArguments(cx, argc, argv, "s", &path))
		return JS_FALSE;

	/* create result array */
	array = JS_NewArrayObject(cx, 0, NULL);
	if (!array)
		return JS_FALSE;
	*rval = OBJECT_TO_JSVAL(array);

	/* open directory */
	dir = opendir(makepath(path));
	if (dir == NULL) {
		js_raise(cx, "Cannot read directory `%s'", path);
		return JS_FALSE;
	}

	/* iterate over directory, reading entries, and appending
	 * names to the result array */
	index = 0;
	while ((d = readdir(dir)) != NULL) {
		if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0)
			continue;

		name = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, d->d_name));
		JS_SetElement(cx, array, index++, &name);
	}

	/* cleanup */
	closedir(dir);

	return JS_TRUE;
}

/* ==== INITIALIZE BINDINGS ==== */

int init_filelib (void)
{
	JSObject* ns_file;

	/* create "namespace" object */
	if ((ns_file = JS_DefineObject(js_context, js_global, "File", NULL, NULL, JSPROP_ENUMERATE|JSPROP_PERMANENT)) == NULL) {
		log_error("JS_DefineObject() failed");
		return 0;
	}

	/* create all our custom functions */
	if (!JS_DefineFunctions(js_context, ns_file, js_file_functions)) {
		log_error("JS_DefineFunctions() failed");
		return 0;
	}

	return 1;
}

/* vim: set tabstop=2 shiftwidth=2 ai: */
